package monsmon.util;

import java.util.*;
import java.io.*;
import java.util.regex.*;
import monsmon.term.Key;

/*
 * Parses a keydefinitions (.key) file.
 * Format:
 *	
 *	#global definitions
 *	<action> : [C-][S-][M-][A-]<key>	#standard definition
 *	[normal_state]			#use a diffrent state
 *	inventory_open	: S-I		#inventory opens on I
 *	check_status	: C-~F12	#the '~' allows specialty keys
 *
 *
 *
 */
public class KeyConfigParser{

	public static class KeyCommand{
		public final String command;
		public final int keycode, comm_code;
		public KeyCommand(String cmd, int kc){
			command = cmd;
			keycode = kc;
			comm_code = 0;
		}
	}

	public static HashMap<String, List<KeyCommand>> 
		parse_config(String fname){
		HashMap<String, List<KeyCommand>> hm = null;
		try{
			hm = parse_config(FileLocator.get_read_file(fname));
		}catch(KeyConfigFormatException ex){
			throw ex;
		}
		return hm;
	}

	private static final String 
		empty_line_s	=	"^\\s*$",
		seg_dec_s	=	"^\\s*\\[(\\S+|~global)\\]\\s*$",
		key_bind_s	=	"^\\s*(\\S+)\\s*:\\s*"
				+	"(.-|)(.-|)(.-|)(.-|)(.|~\\S+)\\s*$",
		comment_s	=	"#.*$";
	private static final Pattern
		empty_line	=	Pattern.compile(empty_line_s),
		seg_dec		=	Pattern.compile(seg_dec_s),
		key_bind	=	Pattern.compile(key_bind_s),
		comment		=	Pattern.compile(comment_s);


	/*
	 * Reads a keyconfig from InputStream file.  
	 * This function does not close the InputStream, the client 
	 *  must manage it.
	 * Returns null if there is an IO error reading the config.
	 */
	public static HashMap<String, List<KeyCommand>> 
		parse_config(InputStream file){
		if(file == null){
			throw new KeyConfigFormatException("File is null");
		}
		InputStreamReader inrdr = new InputStreamReader(file);
		BufferedReader in = new BufferedReader(inrdr);
		String line = null;
		HashMap<String, List<KeyCommand>> keymap = new 
			HashMap<String, List<KeyCommand>>();
		String current_seg = "~global";
		keymap.put(current_seg,
			new ArrayList<KeyCommand>());
		int ln = 0;
		while(true){
			ln++;
			try{
				line = in.readLine();
			}catch(IOException ex){
				System.err.print("Error reading KeyConfig:");
				ex.printStackTrace();
				return null;
			}
			if("".equals(line)) continue;
			if(line == null) break;	
			//Note: faster than line.replaceAll
			line = comment.matcher(line).replaceAll("");
			Matcher m;
			m = key_bind.matcher(line);
			if(m.matches()){
				List<KeyCommand> l = keymap.get(current_seg);
				l.add(new KeyCommand(m.group(1), 
					get_key_match(m, ln)));
				continue;
			}
			m = empty_line.matcher(line);
			if(m.matches()){
				continue;
			}
			m = seg_dec.matcher(line);
			if(m.matches()){
				current_seg = m.group(1);
				keymap.put(current_seg, 
					new ArrayList<KeyCommand>());
			}else{
				throw new KeyConfigFormatException(
					"Error at line: "+ln);
			}
		}
		return keymap;
	}
	
	/*
	 * Exception raised when there is malformed syntax in the 
	 *  KeyConfig file/stream
	 */
	public static class KeyConfigFormatException extends RuntimeException{
		
		private static final long serialVersionUID = 3664664394591867435L;

		public KeyConfigFormatException(String s){
			super(s);
		}
	
	}

	private static int get_key_match(Matcher m, int line){
		boolean a = false, c = false, meta = false, s = false;
		String mods[] = new String[4];
		mods[0] = m.group(2);
		mods[1] = m.group(3);
		mods[2] = m.group(4);
		mods[3] = m.group(5);
		for(String mod : mods){
			if(mod == null) continue;
			if("".equals(mod)) continue;
			switch(mod.charAt(0)){
				case 'A':
				case 'a':
					a = true;
					break;
				case 'C':
				case 'c':
					c = true;
					break;
				case 'S':
				case 's':
					s = true;
					break;
				case 'M':
				case 'm':
					meta = true;
					break;
				default:
					throw new KeyConfigFormatException(
						"Bad modifier: "+mod.charAt(0)
						+ " @"+line);
			}
		}
		String key = m.group(6);
		char fchar = key.charAt(0);
		if(fchar != '~'){
			return Key.to_key(fchar, a, c, meta, s);
		}else if(key.length() >= 2 && key.charAt(1) == '~'){
			return Key.to_key(fchar, a, c, meta, s);
		}else{
			key = "K_" + key.substring(1).toUpperCase();
			int kcode = Key.to_key(key, a, c, meta, s);
			if(kcode != Key.BAD_KEY) return kcode;
			throw new KeyConfigFormatException("Bad specifier: "
				+ key + " @" + line);
		}
	}
}





